//----------------------------------------------------------------------------
//  ,-.
// /_\___ ,   Ninja Framework
// '  ___\    (C) Riccardo Capra - http://riccar.do
//  //   \\
//----------------------------------------------------------------------------
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//----------------------------------------------------------------------------

#ifndef NJ_FUNDAMENTAL_MACROS_H
#	define NJ_FUNDAMENTAL_MACROS_H

#	include "NjNinjaSyncLib.h"
#	include "NjConfiguration.h"
#	include "NjFundamentalTypes.h"

/// \def NJ_NULL
/// \brief A NULL pointer

#	if NJ_COMPILER_TYPE_VC
#		define NJ_NULL nullptr
#	elif NJ_COMPILER_TYPE_GCC
#		define NJ_NULL __null
#	else
#		error "Unsupported build configuration"
#	endif

/// \brief Used to disable compile warnings when function parameters are not used
/// \param xArgument the function argument to be ignored
#	define NJ_UNUSED_VAR(xArgument) { (void)xArgument; }

/// \brief Concatenates two tokens
#	define NJ_CONCATENATE(xArgument01, xArgument02) __NJ_CONCATENATE(xArgument01, xArgument02)
#	define ____NJ_CONCATENATE(xArgument01, xArgument02) xArgument01 ## xArgument02
#	define __NJ_CONCATENATE(xArgument01, xArgument02) ____NJ_CONCATENATE(xArgument01, xArgument02)

/// \brief Converts a token into an 8 bit string
#	define NJ_TO_STRING8(xArgument) #xArgument

/// \brief Converts a token into an 16 bit string
#	define NJ_TO_STRING16(xArgument) L #xArgument

/// \brief Generates an unique id with prefix \em xArgument
#	define NJ_UNIQUE_ID(xArgument) __NJ_UNIQUE_ID(xArgument, __LINE__)
#	define __NJ_UNIQUE_ID(xArgument01, xArgument02) NJ_CONCATENATE(xArgument01, xArgument02)

/// \brief Returns the length of a fixed size array
#	define NJ_LENGTH_OF(aArray) (sizeof(aArray)/sizeof(aArray[0]))

/// \def NJ_DEPRECATED
/// \brief Used to mark functions/methods as deprecated

#	if NJ_COMPILER_TYPE_VC
#		define NJ_DEPRECATED __declspec(deprecated)
#	elif NJ_COMPILER_TYPE_GCC
#		define NJ_DEPRECATED __attribute__ ((deprecated))
#	else
#		error "Unsupported build configuration"
#	endif

/// \def NJ_ALIGN
/// \brief Defines the alignment of a type

#	if NJ_COMPILER_TYPE_VC
#		define NJ_ALIGN(uBytes) __declspec(align(uBytes))
#	elif NJ_COMPILER_TYPE_GCC
#		define NJ_ALIGN(uBytes) __attribute__ ((aligned (uBytes)))
#	else
#		error "Unsupported build configuration"
#	endif

/// \brief Returns the alignment of a type
#	define NJ_ALIGN_OF(tType) __alignof(tType)

/// \brief Calculate the offset of an attribute within a class
#	define NJ_OFFSET_OF(tClass, xAttribute) (reinterpret_cast<NjUint>(&(reinterpret_cast<tClass *>(0x4EADBEE0)->xAttribute))-0x4EADBEE0)

/// \def NJ_COMPILE_ASSERT_MSG
/// \brief Set a compile-time assertion
/// \param bCondition The condition to be verified at compile time
/// \param szMessage The message to be print if the condition is not satisfied

/// \def NJ_COMPILE_ERROR_MSG
/// \brief Generates a compile error
/// \param szMessage The message to be print

#	if NJ_COMPILER_TYPE_VC
#		define NJ_COMPILE_ASSERT_MSG(bCondition, szMessage)	static_assert(bCondition, szMessage "[" NJ_TO_STRING8(bCondition) "]" );
#		define NJ_COMPILE_ERROR_MSG(szMessage)	static_assert(NJ_FALSE, szMessage);
#	elif NJ_COMPILER_TYPE_GCC
		template <NjBool bCondition> struct NjCompilerAssert;
		template<> struct NjCompilerAssert<NJ_TRUE> { };

#		define NJ_COMPILE_ASSERT_MSG(bCondition, szMessage)							\
			static NjCompilerAssert<bCondition> NJ_UNIQUE_ID(g_kCompilerAssert);	\
			NJ_UNUSED_VAR(NJ_UNIQUE_ID(g_kCompilerAssert));
#		define NJ_COMPILE_ERROR_MSG(szMessage)	NJ_COMPILE_ASSERT_MSG(NJ_FALSE, szMessage);
#	else
#		error "Unsupported build configuration"
#	endif

/// \brief Set a compile-time assertion
/// \param bCondition The condition to be verified at compile time
#	define NJ_COMPILE_ASSERT(bCondition)	NJ_COMPILE_ASSERT_MSG(bCondition, "Compile assetion ");

/// \brief Generates a compile error
#	define NJ_COMPILE_ERROR()	NJ_COMPILE_ERROR_MSG("Compile error");

/// \brief Generates a compile warning
/// \param szMessage The message to be print
#	define NJ_COMPILE_WARNING_MSG(szMessage) __NJ_COMPILE_WARNING_MSG(__FILE__, __LINE__, szMessage);
#	define __NJ_COMPILE_WARNING_MSG(strFile, uLine, szMessage) __pragma(message(strFile "(" NJ_TO_STRING8(uLine) "): warning NJ: " szMessage))

/// \brief Generates a compile warning
#	define NJ_COMPILE_WARNING() NJ_COMPILE_WARNING_MSG("Compile warning");

/// \brief Generates a "to do" message
#	define NJ_TODO() NJ_COMPILE_WARNING_MSG("To do " __FUNCTION__)

/// \brief Used in .cpp files that do not implement any method/function
#	define NJ_DUMMY_CPP_FILE(xFileId) void NJ_CONCATENATE(NjDummyFunction_, xFileId)() { }

/// \brief Makes the default constructor not accessible
/// \param tType The name of the class
#	define NJ_DISABLE_DEFAULT_CONSTRUCTOR(tType)	\
	private:										\
		tType();

/// \brief Makes the destructor not accessible
/// \param tType The name of the class
#	define NJ_DISABLE_DESTRUCTOR(tType)				\
	private:										\
		~tType();

/// \brief Makes the copy constructor not accessible
/// \param tType The name of the class
#	define NJ_DISABLE_COPY_CONSTRUCTOR(tType)		\
	private:										\
		tType(tType const & kCopy);

/// \brief Makes the assignment operator not accessible
/// \param tType The name of the class
#	define NJ_DISABLE_ASSIGNMENT_OPERATOR(tType)	\
	private:										\
		tType & operator=(tType const & kCopy);

/// \brief Disables copy operations on the class
/// \param tType The name of the class
#	define NJ_DISABLE_COPY(tType)					\
	NJ_DISABLE_COPY_CONSTRUCTOR(tType);				\
	NJ_DISABLE_ASSIGNMENT_OPERATOR(tType)

/// \brief Disables class instancing
/// \param tType The name of the class
#	define NJ_DISABLE_INSTANCE(tType)				\
	NJ_DISABLE_DEFAULT_CONSTRUCTOR(tType);			\
	NJ_DISABLE_DESTRUCTOR(tType);					\
	NJ_DISABLE_COPY(tType)

/// \brief Implements a trivial default constructor
/// \param tType The name of the class
#	define NJ_TRIVIAL_DEFAULT_CONSTRUCTOR(tType)	\
		tType()										\
		{											\
		}

/// \brief Implements a trivial destructor
/// \param tType The name of the class
#	define NJ_TRIVIAL_DESTRUCTOR(tType)				\
		~tType()									\
		{											\
		}

/// \brief Implements a trivial destructor (virtual)
/// \param tType The name of the class
#	define NJ_TRIVIAL_VIRTUAL_DESTRUCTOR(tType)		\
		virtual NJ_TRIVIAL_DESTRUCTOR(tType)

/// \brief Implements a trivial copy constructor
///
/// Nothing is copied
/// \param tType The name of the class
#	define NJ_TRIVIAL_COPY_CONSTRUCTOR(tType)		\
		tType(tType const & kCopy)					\
		{											\
			NJ_UNUSED_VAR(kCopy);					\
		}

/// \brief Implements a trivial assignment operator
///
/// Nothing is copied
/// \param tType The name of the class
#	define NJ_TRIVIAL_ASSIGNMENT_OPERATOR(tType)	\
		tType & operator=(tType const & kCopy)		\
		{											\
			NJ_UNUSED_VAR(kCopy);					\
			return (*this);							\
		}

/// \brief Implements trivial copy operations
///
/// Nothing is copied
/// \param tType The name of the class
#	define NJ_TRIVIAL_COPY(tType)					\
	NJ_TRIVIAL_COPY_CONSTRUCTOR(tType)				\
	NJ_TRIVIAL_ASSIGNMENT_OPERATOR(tType)

/// \brief Dynamically allocates a type \em tType
#	define NjNew(tType) (::new tType)

/// \brief Dynamically allocates an array of type \em tType
///
/// Usage should be: NjNewArray(NjUint[10]);
#	define NjNewArray(tType) NjNew(tType)

/// \brief Destroys a dynamically allocated type \em pPtr
#	define NjDelete(pPtr) ::delete pPtr

/// \brief Destroys an dynamically allocated array of types \em pPtr
#	define NjDeleteArray(pPtr) ::delete[] pPtr

/// \def NJ_THREAD_DATA
/// \brief Declares the static/global variable as an entry in the Thread Local Storage (TLS)

#	if NJ_COMPILER_TYPE_VC
#		define NJ_THREAD_DATA __declspec(thread)
#	elif NJ_COMPILER_TYPE_GCC
#		define NJ_THREAD_DATA __thread
#	else
#		error "Unsupported build configuration"
#	endif

/// \def NJ_FORCE_INLINE
/// \brief Forces inline compilation
#	if NJ_COMPILER_TYPE_VC
#		define NJ_FORCE_INLINE __forceinline
#	elif NJ_COMPILER_TYPE_GCC
#		define NJ_FORCE_INLINE inline __attribute__ ((always_inline))
#	else
#		error "Unsupported build configuration"
#	endif

#endif // NJ_FUNDAMENTAL_MACROS_H
